using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using Common.Core;

namespace Common.Unity.Extensions.PropertyMapper
{
	static class PropertyMappingExpressionContextResolver
	{
		static readonly char[] 
			expressionPartSeparator = new char[] { '.' }, 
			indexExprStartChars = new char[] { '[', '(' },
			indexExprEndChars = new char[] { ']', ')' };

		#region DataBinding Hackist:
		public static LinkedList<PropertyMappingExpressionContext> Eval( object container, string expression )
		{
			Guard.IsNotNull( expression, "expression" );
			expression = expression.Trim();
			Guard.IsNotNullOrEmpty( expression, "expression" );
			if ( container == null )
			{
				return null;
			}
			string[] expressionParts = expression.Split( expressionPartSeparator );
			return Eval( container, expressionParts );
		}

		static LinkedList<PropertyMappingExpressionContext> Eval( object container, string[] expressionParts )
		{
			LinkedList<PropertyMappingExpressionContext> result = new LinkedList<PropertyMappingExpressionContext>();
			object propertyValue = container;
			for ( int i = 0; ( i < expressionParts.Length ) && ( propertyValue != null ); i++ )
			{
				string propName = expressionParts[i];
				PropertyInfo descriptor;
				object currentContainer = propertyValue;
				object[] index = null;
				propertyValue = propName.IndexOfAny( indexExprStartChars ) < 0 ? GetPropertyValue( propertyValue, propName, out descriptor ) : GetIndexedPropertyValue( propertyValue, propName, out descriptor, out index );
				result.AddLast( new PropertyMappingExpressionContext( currentContainer, descriptor, propertyValue, index ) );
			}
			return result;
		}

		static object GetPropertyValue( object container, string propName, out PropertyInfo descriptor )
		{
			Guard.IsNotNull( container, "container" );
			Guard.IsNotNull( propName, "propName" );
			descriptor = container.GetType().GetProperty( propName, CommonBindingFlags.AllProperties );
			if ( descriptor == null )
			{
				throw new InvalidOperationException( string.Format( "Property '{0}' Not found on type '{1}'", propName, container.GetType().FullName ) );
			}
			return descriptor.GetValue( container, null );
		}

		static object GetIndexedPropertyValue( object container, string expr, out PropertyInfo descriptor, out object[] index )
		{
			Guard.IsNotNull( container, "container" );
			Guard.IsNotNullOrEmpty( expr, "expr" );

			index = null;
			descriptor = null;
			bool flag = false;
			int length = expr.IndexOfAny( indexExprStartChars );
			int num2 = expr.IndexOfAny( indexExprEndChars, length + 1 );
			if ( ( ( length < 0 ) || ( num2 < 0 ) ) || ( num2 == ( length + 1 ) ) )
			{
				throw new ArgumentException( string.Format( "Invalid Indexed Expression: {0}.", new object[] { expr } ) );
			}
			string propName = null;
			object obj3 = null;
			string s = expr.Substring( length + 1, ( num2 - length ) - 1 ).Trim();
			if ( length != 0 )
			{
				propName = expr.Substring( 0, length );
			}
			if ( s.Length != 0 )
			{
				if ( ( ( s[0] == '"' ) && ( s[s.Length - 1] == '"' ) ) || ( ( s[0] == '\'' ) && ( s[s.Length - 1] == '\'' ) ) )
				{
					obj3 = s.Substring( 1, s.Length - 2 );
				}
				else if ( char.IsDigit( s[0] ) )
				{
					int result;
					flag = int.TryParse( s, NumberStyles.Integer, CultureInfo.InvariantCulture, out result );
					if ( flag )
					{
						obj3 = result;
					}
					else
					{
						obj3 = s;
					}
				}
				else
				{
					obj3 = s;
				}
			}
			if ( obj3 == null )
			{
				throw new ArgumentException( string.Format( "Invalid Indexed Expression: {0}.", new object[] { expr } ) );
			}
			object propertyValue;
			if ( ( propName != null ) && ( propName.Length != 0 ) )
			{
				propertyValue = GetPropertyValue( container, propName, out descriptor );
			}
			else
			{
				propertyValue = container;
			}
			if ( propertyValue == null )
			{
				return null;
			}
			Array array = propertyValue as Array;
			if ( ( array != null ) && flag )
			{
				return array.GetValue( (int)obj3 );
			}
			if ( ( propertyValue is IList ) && flag )
			{
				return ( (IList)propertyValue )[(int)obj3];
			}
			PropertyInfo info =
				propertyValue.GetType().GetProperty( "Item", BindingFlags.Public | BindingFlags.Instance, null, null,
				                                     new Type[] { obj3.GetType() }, null );
			if ( info == null )
			{
				throw new ArgumentException(
					string.Format( "No Indexed Accessor: {0}", new object[] { propertyValue.GetType().FullName } ) );
			}
			index = new object[] { obj3 };
			return info.GetValue( propertyValue, index );
		}
		#endregion
	}
}